/********************************************************************************
 * Project Name		[JavaSE_Fundamentals]
 * File Name     	[ClassScannerUtils.java]
 * Creation Date 	[2015-01-01]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ********************************************************************************/
package jdk5.annotation.cases.utils;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 扫描某个包[package/jar/zip]下的所有的类
 * 
 * @author 不落的太阳(Sean Yang aka ShortPeace)
 * @version 1.0
 * @since jdk 1.8
 * 
 */
public class ClassScannerUtils {

	private static List<File> files = new ArrayList<>();

	/**
	 * 获得指定package中所有的class文件
	 * 
	 * @param packageName
	 *            包名
	 * @param packagePath
	 *            包路径
	 * @param isRecursive
	 *            是否递归查找
	 * @param classes
	 *            存放找到的class文件的集合
	 */
	private static void getClassesFromPackage(String packageName, String packagePath, final boolean isRecursive,
			Set<Class<?>> classes) {
		File packageDirectory = new File(packagePath);
		// 如果不存在或不是目录, 直接返回
		if (!packageDirectory.exists() || !packageDirectory.isDirectory()) {
			return;
		}

		// 如果存在, 使用文件过滤器获取包下所有的class文件和子包
		File[] filesInDirectory = packageDirectory.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return (isRecursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});

		// 循环获得的所有文件[classes + packages]
		for (File file : filesInDirectory) {
			// 如果是目录, 继续查找
			if (file.isDirectory()) {
				getClassesFromPackage(packageName + "." + file.getName(), file.getAbsolutePath(), isRecursive, classes);
			} else {
				String className = file.getName().substring(0, file.getName().lastIndexOf("."));
				try {
					classes.add(
							Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 获得指定package中所有的class文件
	 * 
	 * @param filePath
	 * @param isRecursive
	 * @param classes
	 * @throws Exception
	 */
	public static void getClassesFromPackage(String filePath, boolean isRecursive, Set<Class<?>> classes)
			throws Exception {
		String packageName = filePath.replace('.', '/');
		Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName);
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			String protocol = url.getProtocol();
			if ("file".equals(protocol)) {
				String path = URLDecoder.decode(url.getPath(), "UTF-8");
				getClassesFromPackage(packageName, path, isRecursive, classes);
			}
		}
	}

	/**
	 * 通过指定的路径加载jar文件
	 * 
	 * @param path
	 * @throws Exception
	 */
	public static void getClassesFromJar(String path, final boolean isRecursive, Set<Class<?>> classes)
			throws Exception {
		loadFiles(path);
		if (files.size() == 0) {
			return;
		}

		for (File file : files) {
			@SuppressWarnings("resource")
			JarFile jarFile = new JarFile(file);
			Enumeration<JarEntry> jarEntries = jarFile.entries();
			while (jarEntries.hasMoreElements()) {
				JarEntry jarEntry = jarEntries.nextElement();
				String name = jarEntry.getName();

				String packageName = "";

				int index = name.lastIndexOf('/');
				// 如果name是目录名
				if (index != -1) {
					packageName = name.substring(0, index).replaceAll("/", ".");
				}

				if (index != -1 && isRecursive) {
					if (name.endsWith(".class") && !jarEntry.isDirectory()) {
						String className = name.substring(packageName.length() + 1, name.lastIndexOf('.'));
						// 过滤掉有些不存在的类
						if (jarEntry.getSize() > 0) {
							classes.add(Thread.currentThread().getContextClassLoader()
									.loadClass(packageName + "." + className));

						}
					}
				}
			}
		}
	}

	/**
	 * 加载指定路径的Jar文件
	 * 
	 * @param path
	 * @throws Exception
	 */
	private static void loadFiles(String path) throws Exception {
		File file = new File(path);
		loadFiles(file);
	}

	/**
	 * 加载指定文件夹的所有jar/zip文件
	 * 
	 * @param file
	 * @throws Exception
	 */
	private static void loadFiles(File directory) throws Exception {

		if (directory.isDirectory()) {
			File[] files = directory.listFiles();
			for (File file : files) {
				loadFiles(file);
			}
		} else {
			if (directory.getAbsolutePath().endsWith(".jar") || directory.getAbsolutePath().endsWith(".zip")) {
				files.add(directory);
			}
		}
	}

	/**
	 * 根据指定的文件路径获得所有的Class文件
	 * 
	 * @param filePath
	 * @return
	 * @throws Exception
	 */
	public static Set<Class<?>> getClasses(String filePath) throws Exception {
		Set<Class<?>> classes = new LinkedHashSet<>();
		boolean isRecursive = true;

		boolean isJarFile = filePath.indexOf("jar") != -1 || filePath.indexOf("zip") != -1;

		if (isJarFile) {
			getClassesFromJar(filePath, isRecursive, classes);
		} else {
			getClassesFromPackage(filePath, isRecursive, classes);
		}

		return classes;
	}
}
